﻿/**************************************************************
 *
 * 唯一标识：52247a56-0d9a-4a0f-8f7e-00ab57aa9864
 * 命名空间：Sgr.Utilities
 * 创建时间：2023/8/5 11:44:37
 * 机器名称：DESKTOP-S0D075D
 * 创建者：antho
 * 电子邮箱：fengqinhua2016@163.com
 * 描述：程序集操作工具类
 *
 *  // 加载程序集
 *  var assemblies = AssemblyHelper.LoadAssemblies(@"C:\MyApp\Plugins", SearchOption.TopDirectoryOnly);
 *
 *  // 获取实现了特定接口的类型
 *  var implementingTypes = AssemblyHelper.GetImplementedTypes<IMyInterface>(assembly);
 *
 *  // 获取带有特定特性的类型
 *  var attributedTypes = AssemblyHelper.GetTypesWithAttribute<MyAttribute>(assembly);
 *
 *  // 获取当前域中的所有程序集
 *  var domainAssemblies = AssemblyHelper.GetCurrentDomainAssemblies();
 *
 *  // 获取程序集中的所有类型
 *  var allTypes = AssemblyHelper.GetAllTypes(assembly);
 *
 **************************************************************/

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;

namespace Sgr.Utilities
{
    /// <summary>
    /// 程序集操作帮助类
    /// </summary>
    public static class AssemblyHelper
    {
        /// <summary>
        /// 从指定文件夹加载程序集
        /// </summary>
        /// <param name="folderPath">文件夹路径</param>
        /// <param name="searchOption">搜索选项</param>
        /// <returns>加载的程序集列表</returns>
        /// <exception cref="DirectoryNotFoundException">指定的文件夹不存在</exception>
        public static List<Assembly> LoadAssemblies(string folderPath, SearchOption searchOption)
        {
            Check.NotNull(folderPath, nameof(folderPath));

            if (!Directory.Exists(folderPath))
                throw new DirectoryNotFoundException($"目录不存在: {folderPath}");

            var assemblyFiles = GetAssemblyFiles(folderPath, searchOption);
            var assemblies = new List<Assembly>();

            foreach (var file in assemblyFiles)
            {
                try
                {
                    var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(file);
                    assemblies.Add(assembly);
                }
                catch (BadImageFormatException)
                {
                    // 跳过非.NET程序集
                    continue;
                }
                catch (FileLoadException)
                {
                    // 跳过已加载的程序集
                    continue;
                }
            }

            return assemblies;
        }

        /// <summary>
        /// 获取指定文件夹中的程序集文件路径
        /// </summary>
        /// <param name="folderPath">文件夹路径</param>
        /// <param name="searchOption">搜索选项</param>
        /// <returns>程序集文件路径集合</returns>
        /// <exception cref="DirectoryNotFoundException">指定的文件夹不存在</exception>
        public static IEnumerable<string> GetAssemblyFiles(string folderPath, SearchOption searchOption)
        {
            Check.NotNull(folderPath, nameof(folderPath));

            if (!Directory.Exists(folderPath))
                throw new DirectoryNotFoundException($"目录不存在: {folderPath}");

            return Directory
                .EnumerateFiles(folderPath, "*.*", searchOption)
                .Where(s => s.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) ||
                           s.EndsWith(".exe", StringComparison.OrdinalIgnoreCase));
        }

        /// <summary>
        /// 获取程序集中的所有类型
        /// </summary>
        /// <param name="assembly">要检查的程序集</param>
        /// <returns>程序集中的所有可访问类型列表</returns>
        /// <remarks>
        /// 此方法会尝试加载程序集中的所有类型。如果某些类型无法加载（例如依赖项缺失），
        /// 这些类型将被忽略，但会返回所有可以成功加载的类型。
        /// </remarks>
        /// <exception cref="ArgumentNullException">当 assembly 为 null 时抛出</exception>
        public static IReadOnlyList<Type> GetAllTypes(Assembly assembly)
        {
            Check.NotNull(assembly, nameof(assembly));

            try
            {
                // 直接尝试获取所有类型
                return assembly.GetTypes();
            }
            catch (ReflectionTypeLoadException ex)
            {
                // 处理类型加载异常，收集所有可以加载的类型
                var loadedTypes = new List<Type>();
                for (int i = 0; i < ex.Types.Length; i++)
                {
                    var type = ex.Types[i];
                    if (type != null)
                    {
                        loadedTypes.Add(type);
                    }
                }

                if (loadedTypes.Count == 0)
                    System.Diagnostics.Debug.WriteLine($"无法从程序集 {assembly.FullName} 加载任何类型");

                return loadedTypes.AsReadOnly();
            }
            catch (Exception ex) when (ex is not ArgumentNullException)
            {
                // 处理其他可能的异常（如 SecurityException, BadImageFormatException 等）
                System.Diagnostics.Debug.WriteLine($"从程序集 {assembly.FullName} 加载类型时发生异常：{ex.Message}");
                return Array.Empty<Type>();
            }
        }

        /// <summary>
        /// 获取程序集中实现了指定接口的所有类型
        /// </summary>
        /// <typeparam name="TInterface">接口类型</typeparam>
        /// <param name="assembly">程序集</param>
        /// <returns>实现了指定接口的类型列表</returns>
        public static IEnumerable<Type> GetImplementedTypes<TInterface>(Assembly assembly)
        {
            Check.NotNull(assembly, nameof(assembly));

            return GetAllTypes(assembly)
                .Where(t => t is { IsClass: true, IsAbstract: false } &&
                           typeof(TInterface).IsAssignableFrom(t));
        }

        /// <summary>
        /// 获取程序集中带有指定特性的所有类型
        /// </summary>
        /// <typeparam name="TAttribute">特性类型</typeparam>
        /// <param name="assembly">程序集</param>
        /// <returns>带有指定特性的类型列表</returns>
        public static IEnumerable<Type> GetTypesWithAttribute<TAttribute>(Assembly assembly)
            where TAttribute : Attribute
        {
            Check.NotNull(assembly, nameof(assembly));

            return GetAllTypes(assembly)
                .Where(t => t.GetCustomAttribute<TAttribute>() != null);
        }

        /// <summary>
        /// 获取当前应用程序域中的所有程序集
        /// </summary>
        /// <returns>程序集列表</returns>
        public static IEnumerable<Assembly> GetCurrentDomainAssemblies()
        {
            //var set = new HashSet<Assembly>();
            //try
            //{
            //    foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
            //    {
            //        set.Add(a);
            //    }
            //}
            //catch
            //{
            //    // 忽略不可预期的错误，继续尝试从 ALC 获取
            //}

            //try
            //{
            //    foreach (var alc in AssemblyLoadContext.All)
            //    {
            //        foreach (var a in alc.Assemblies)
            //        {
            //            set.Add(a);
            //        }
            //    }
            //}
            //catch
            //{
            //    // 忽略以提高稳健性（某些环境下可能受限）
            //}

            //return set.ToArray();

            return AppDomain.CurrentDomain.GetAssemblies();
        }
    }
}